/**
  * @(#)DefaultResourceExtractor.java
  *
  * Manage the materialization of local resource files from the source archives
  *
  * @author Course Scheduler Team
  * 
  * @license GNU General Public License version 3 (GPLv3)
  *
  * This file is part of Course Scheduler, an open source, cross platform
  * course scheduling tool, configurable for most universities.
  *
  * Copyright (C) 2010-2012 Mike Reinhold
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation, either version 3 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  * 
  */
package com.pollicitus.scheduler.util;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.DirectoryStream;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * Manage the materialization of local resource files from the source archives
 * 
 * @author Course Scheduler Team
 *
 */
public class DefaultResourceExtractor {
	
	
	/**
	 * The first resource archive search directory
	 */
	private static final String SEARCH_DIRECTORY_1 = "./bin";
	
	/**
	 * The resource archive glob string. Only archives which match this patter will be extracted
	 */
	private static final String RESOURCE_ARCHIVE = "course-scheduler-resources-*.jar";
	
	/**
	 * The exclusion pattern regex which is used to validate whether a file should be extracted
	 */
	private static final String EXCLUSION_PATTERN =".*META-INF.*";
	
	/**
	 * Materialize all local files from the default resource archives
	 * 
	 * @throws IOException
	 */
	public static void materializeLocalFiles() throws IOException {
		System.out.println("\nPreparing to extract default resources");
		
		Path directory = FileSystems.getDefault().getPath(SEARCH_DIRECTORY_1);
		try (DirectoryStream<Path> flow = Files.newDirectoryStream(directory, RESOURCE_ARCHIVE)) {
			
			// materialize the resource files from each resource archive
			for(Path item: flow) {
				materializeLocalFiles(item.toString());
			}
		}
	}
	
	/**
	 * Materialize all local files from the specified resource archive
	 * 
	 * @param sourceArchive the source archive to materialize local resources from
	 * @throws IOException 
	 */
	public static void materializeLocalFiles(String sourceArchive) throws IOException{
		JarFile archive = new JarFile(sourceArchive);
		Enumeration<JarEntry> entries = archive.entries();
		
		System.out.println("Extracting resources from " + archive.getName());
		
		//check each entry
		while(entries.hasMoreElements()){
			JarEntry entry = entries.nextElement();
			
			//check entry extraction validity
			if(includeEntry(entry)){
				System.out.println("Extracting " + entry.toString() + " from " + archive.getName());
				materializeLocalFile(archive, entry);
			}else{
				System.out.println("Ignoring resource " + entry.toString() + " in " + archive.getName());
			}
		}
	}
	
	/**
	 * Materialize the specified resource from the source archive. The resource will be
	 * materialized relative to the current working directory
	 * 
	 * @param sourceArchive path of the source archive where the resource can be found
	 * @param resource the relative path and name of the resource to materialize
	 * @throws IOException 
	 */
	public static void materializeLocalFile(String sourceArchive, String resource) throws IOException{
		JarFile archive = new JarFile(sourceArchive);
		JarEntry entry = archive.getJarEntry(resource);
		
		//check entry extraction validity
		if(includeEntry(entry)){
			System.out.println("Extracting " + entry.toString() + " from " + archive.getName());
			materializeLocalFile(archive, entry);
		}else{
			System.out.println("Ignoring resource " + entry.toString() + " in " + archive.getName());
		}
	}
	
	/**
	 * Verify that the Jar Entry does not match the exclusion pattern
	 * 
	 * @param entry the entry in the jar to validate
	 * @return if the jar should be extracted (the entry does NOT match the exclusion pattern)
	 */
	private static boolean includeEntry(JarEntry entry){
		return !entry.toString().matches(EXCLUSION_PATTERN);
	}
	
	/**
	 * Extract the file from the archive to the target directory. If the entry is a directory,
	 * create that new directory. If it is a file, read it from the jar and write it to the directory.
	 * 
	 * @param archive
	 * @param resource
	 * @throws IOException 
	 */
	private static void materializeLocalFile(JarFile archive, JarEntry resource) throws IOException{
		
		//Check if the requested resource is a directory and if so, create
		if(resource.isDirectory()){
			File target = new File(resource.getName());
			target.mkdir();
		} else {
			//otherwise extract the file			
			try(InputStream input = archive.getInputStream(resource)){
				try(FileOutputStream output = new FileOutputStream(resource.getName());){
					//copy the entire file contents
					while(input.available() > 0) {
						output.write(input.read());
					}
				}
			} 
		}
		
	}
}
